package ru.incoremedia.gwttest.application.client.view.user.notespage;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.Event.NativePreviewHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Widget;
import ru.incoremedia.gwttest.application.client.view.user.noteitem.NoteItem;
import ru.incoremedia.gwttest.application.client.view.user.notesgroup.NotesGroup;
import ru.incoremedia.gwttest.core.client.component.autosizedtextarea.TextChangeHandler;


public class NotesPage extends Composite {

    public interface Styles extends CssResource {
    }

    @UiField Styles style;

    interface MyUiBinder extends UiBinder<Widget, NotesPage> {}

    private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class);

    @UiField FlowPanel groupsContainer;

    private NoteItem sourceNoteItem, targetNoteItem;

    private NotesGroup sourceNotesGroup, targetNotesGroup;

    public NotesPage() {
        initWidget(uiBinder.createAndBindUi(this));

        addNewGroup();

        Event.addNativePreviewHandler(new NativePreviewHandler() {
            @Override
            public void onPreviewNativeEvent(final NativePreviewEvent event) {
                final int eventType = event.getTypeInt();
                switch (eventType) {
                    case Event.ONMOUSEMOVE:
                        showPopupWidget(event.getNativeEvent());
                        break;

                    case Event.ONMOUSEUP:
                        if(targetNoteItem != null) {
                            stopDraggingNoteItem();
                        }

                        if(targetNotesGroup != null) {
                            stopDraggingNotesGroup();
                        }

                        break;
                }
            }
        });

        Window.addWindowScrollHandler(new Window.ScrollHandler() {
            @Override
            public void onWindowScroll(Window.ScrollEvent event) {
                if(targetNoteItem != null) {
                    PopupWidget.show(PopupWidget.getX(), PopupWidget.getY());
                }
            }
        });
    }

    private void showPopupWidget(NativeEvent event) {
        if(targetNoteItem != null || targetNotesGroup != null) {
            event.preventDefault();
            PopupWidget.show(event.getClientX(), event.getClientY());
        }
    }

    private void addNewGroup() {
        final NotesGroup notesGroup = new NotesGroup(this);

        notesGroup.getGroupNameTextBox().addTextChangeHandler(new TextChangeHandler() {
            @Override
            public void onChange() {
                if(notesGroup.getGroupNameTextBox().getText().length() > 0) {
                    if(notesGroup.isNew()) {
                        addNewGroup();
                        notesGroup.setNew(false);
                        notesGroup.addNewNote();
                    }
                }
            }
        });

        notesGroup.getDragZone().addMouseDownHandler(new MouseDownHandler() {
            @Override
            public void onMouseDown(final MouseDownEvent event) {
                event.preventDefault();
                startDraggingNotesGroup(notesGroup, event.getNativeEvent());
            }
        });

        notesGroup.getDragZone().addMouseOverHandler(new MouseOverHandler() {
            @Override
            public void onMouseOver(MouseOverEvent event) {
                selectTargetNotesGroup(notesGroup);
            }
        });

        notesGroup.getGroupNameTextBox().addMouseOverHandler(new MouseOverHandler() {
            @Override
            public void onMouseOver(MouseOverEvent event) {
                selectTargetNotesGroup(notesGroup);
            }
        });

        groupsContainer.add(notesGroup);
        correctTabOrders();
    }

    private void visit(ItemVisitor visitor) {
        for(int i = 0; i < groupsContainer.getWidgetCount(); i++) {
            Widget widget = groupsContainer.getWidget(i);

            if(widget instanceof NotesGroup) {
                NotesGroup notesGroup = (NotesGroup)widget;
                visitor.onVisitNotesGroup(notesGroup);

                for(NoteItem noteItem : notesGroup.getNoteItems()) {
                    visitor.onVisitNoteItem(noteItem);
                }
            }
        }
    }
    
    public void correctTabOrders() {        
        visit(new ItemVisitor() {
            private int count = 100;

            @Override
            public void onVisitNoteItem(NoteItem noteItem) {
                noteItem.getTextArea().setTabIndex(count++);
            }

            @Override
            public void onVisitNotesGroup(NotesGroup notesGroup) {
                notesGroup.getGroupNameTextBox().setTabIndex(count++);
            }
        });
    }

    private void setReadOnly(final boolean readOnly) {
        visit(new ItemVisitor() {
            @Override
            public void onVisitNoteItem(NoteItem noteItem) {
                if(readOnly) {
                    noteItem.enableMoveStyle();
                } else {
                    noteItem.disableMoveStyle();
                }
            }

            @Override
            public void onVisitNotesGroup(NotesGroup notesGroup) {
                if(readOnly) {
                    notesGroup.enableItemMoveStyle();
                } else {
                    notesGroup.disableItemMoveStyle();
                }                
            }
        });
    }

    private void hideAllItems(final boolean hide) {
        visit(new ItemVisitor() {
            @Override
            public void onVisitNoteItem(NoteItem noteItem) {
                if(hide) {
                    noteItem.setVisible(false);
                } else {
                    noteItem.setVisible(true);
                }
            }

            @Override
            public void onVisitNotesGroup(NotesGroup notesGroup) {
                if(hide) {
                    notesGroup.enableGroupMoveStyle();
                } else {
                    notesGroup.disableGroupMoveStyle();
                }
            }
        });
    }

    public void selectTargetNoteItem(NoteItem note) {
        if(targetNoteItem != null) {
            targetNoteItem.disableTargetStyle();
            targetNoteItem = note;
            targetNoteItem.enableTargetStyle();
        }
    }

    public void startDraggingNoteItem(NoteItem noteItem, NativeEvent event) {
        setReadOnly(true);

        sourceNoteItem = noteItem;
        targetNoteItem = noteItem;
        
        PopupWidget.setWidget(createFakeNoteItem(noteItem), "695px");
        showPopupWidget(event);
        selectTargetNoteItem(noteItem);
    }

    public void stopDraggingNoteItem() {
        setReadOnly(false);

        if(!(sourceNoteItem.isNew() && !sourceNoteItem.getNotesGroup().equals(targetNoteItem.getNotesGroup()))) {//если мы не пытаемся перенести новую записку в другую группу
            int index = targetNoteItem.getNotesGroup().getNotesContainer().getWidgetIndex(targetNoteItem);
            sourceNoteItem.getNotesGroup().getNotesContainer().remove(sourceNoteItem);
            targetNoteItem.getNotesGroup().getNotesContainer().insert(sourceNoteItem, index);
            sourceNoteItem.setNotesGroup(targetNoteItem.getNotesGroup());
        }

        targetNoteItem.disableTargetStyle();

        targetNoteItem = null;
        sourceNoteItem = null;

        PopupWidget.hide();

        correctTabOrders();
    }

    private NoteItem createFakeNoteItem(NoteItem noteItem) {
        NoteItem fakeItem = new NoteItem(null);

        if(!noteItem.isNew()) {
            fakeItem.setNew(false);
        }

        if(noteItem.isComplete()) {
            fakeItem.setComplete(true);
        }

        if(noteItem.isDelete()) {
            fakeItem.setDelete(true);
        }
        
        fakeItem.getTextArea().setText(noteItem.getTextArea().getText());
        fakeItem.getTextArea().correctLines();
        return fakeItem;
    }

    private NotesGroup createFakeNotesGroup(NotesGroup notesGroup) {
        NotesGroup fakeGroup = new NotesGroup(null);

        if(!notesGroup.isNew()) {
            fakeGroup.setNew(false);
        }

        if(notesGroup.isDelete()) {
            fakeGroup.setDelete(true);
        }

        fakeGroup.getGroupNameTextBox().setText(notesGroup.getGroupNameTextBox().getText());        
        fakeGroup.enableGroupMoveStyle();
        return fakeGroup;
    }

    public void startDraggingNotesGroup(NotesGroup notesGroup, NativeEvent event) {
        sourceNotesGroup = notesGroup;
        targetNotesGroup = notesGroup;

        hideAllItems(true);

        PopupWidget.setWidget(createFakeNotesGroup(notesGroup), "375px");
        showPopupWidget(event);
        selectTargetNotesGroup(notesGroup);
    }

    public void stopDraggingNotesGroup() {
        hideAllItems(false);

        int index = targetNotesGroup.getNotesPage().getGroupsContainer().getWidgetIndex(targetNotesGroup);
        sourceNotesGroup.getNotesPage().getGroupsContainer().remove(sourceNotesGroup);
        targetNotesGroup.getNotesPage().getGroupsContainer().insert(sourceNotesGroup, index);
        sourceNotesGroup.setNotesPage(targetNotesGroup.getNotesPage());

        targetNotesGroup.disableTargetStyle();

        sourceNotesGroup = null;
        targetNotesGroup = null;

        PopupWidget.hide();

        correctTabOrders();
    }

    private void selectTargetNotesGroup(NotesGroup notesGroup) {
        if(targetNotesGroup != null) {
            targetNotesGroup.disableTargetStyle();
            targetNotesGroup = notesGroup;
            targetNotesGroup.enableTargetStyle();
        }
    }

    public FlowPanel getGroupsContainer() {
        return groupsContainer;
    }
}